{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "87997689",
   "metadata": {},
   "source": [
    "在 LangChain（v0.3 版本）的生态中，Embedding Models（嵌入模型） 是连接 “非结构化文本” 与 “机器可理解的数值向量” 的核心组件，也是实现检索增强生成（RAG）、文本相似度匹配、聚类分析等功能的基础。没有嵌入模型，文本无法转化为向量，后续的向量存储（如 FAISS、Chroma）、语义检索等操作都无法实现。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50fa4a41",
   "metadata": {},
   "source": [
    "# 一、核心背景：什么是文本嵌入？为什么需要它？\n",
    "在理解 LangChain 的 Embedding 组件前，我们需要先明确文本嵌入（Text Embedding） 的本质：\n",
    "\n",
    "- 定义：文本嵌入是将文本（单词、句子、段落甚至文档）映射到低维稠密向量空间的过程。每个文本会对应一个固定长度的数值向量，向量的 “距离”（如余弦距离）直接反映文本的 “语义相似度”—— 两个向量越近，文本语义越相似。\n",
    "- 为什么需要：计算机无法直接理解文本的 “语义”，只能处理数值。通过嵌入，文本的语义信息被编码为向量，从而支持：\n",
    "    1. 语义检索（RAG 的核心）：从海量文档中快速找到与查询语义匹配的内容；\n",
    "    2. 相似度比较：判断两个文本是否同义或相关（如问答匹配、重复内容检测）；\n",
    "    3. 聚类 / 分类：将语义相似的文本归为一类（如用户评论分类）；\n",
    "    4. 特征输入：作为大模型的输入特征（如将文档向量与查询向量拼接后喂给 LLM）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "851b19ec",
   "metadata": {},
   "source": [
    "# 二、LangChain Embedding 组件的核心定位与设计\n",
    "LangChain 本身不开发嵌入模型，而是通过抽象层（BaseEmbeddings 类） 统一了不同嵌入模型的调用接口，实现了 “一次编码，多模型兼容”。其核心价值在于：\n",
    "\n",
    "- 解耦模型与业务逻辑：无论你使用闭源模型（如 OpenAI Embeddings）还是开源模型（如 DeepSeek Embeddings、Sentence-BERT），调用代码几乎无需修改；\n",
    "- 适配 LangChain 生态：嵌入结果可直接对接 LangChain 的向量存储（VectorStores）、检索器（Retrievers）等组件，无需额外格式转换；\n",
    "- 支持批量处理与异步：优化了批量文本嵌入的效率，同时提供异步接口（aembed_documents），适合高并发场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3a29a6d",
   "metadata": {},
   "source": [
    "# 嵌入模型示例\n",
    "嵌入模型分为开源模型与闭源模型，使用闭源嵌入模型时需要使用 api_key，好处是不用额外的存储空间，坏处是调用次数有限制。开源模型不需要 api_key，但调用次数没有限制，但需要部署到本地。\n",
    "\n",
    "## 开源嵌入模型\n",
    "这里我们使用开源嵌入模型 bge-large-zh-v1.5，给出示例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cbab2920",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sentence-BERT 向量维度：1024\n"
     ]
    }
   ],
   "source": [
    "from langchain_huggingface import HuggingFaceEmbeddings\n",
    "\n",
    "embeddings = HuggingFaceEmbeddings(\n",
    "    model_name = \"../../embed_model/bge-large-zh-v1.5\",  # 首次会下载该模型（这里我们提前手动下载好了）\n",
    "    model_kwargs = {\"device\": \"cpu\"},  # 可指定\"cuda\"使用GPU,\n",
    "    encode_kwargs = {\"normalize_embeddings\": True}  # 归一化向量，提升相似度计算准确性\n",
    ")\n",
    "\n",
    "# 生成嵌入\n",
    "text = \"Sentence-BERT 是常用的开源嵌入模型。\"\n",
    "embedding = embeddings.embed_query(text)\n",
    "print(f\"Sentence-BERT 向量维度：{len(embedding)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94aeb0cc",
   "metadata": {},
   "source": [
    "## 示例：对接向量存储（实现语义检索）\n",
    "Embedding 组件的核心用途是为 向量存储（VectorStore） 提供向量数据，进而实现语义检索（RAG 的核心步骤）。以下结合 FAISS（轻量级向量库）演示完整流程：\n",
    "\n",
    "**流程梳理：**\n",
    "  1. 准备原始文档 → 用 TextSplitter 分割文档（复用之前学的组件）；\n",
    "  2. 用 Embedding 模型生成文档向量；\n",
    "  3. 将 “分割后的文档 + 文档向量” 存入 FAISS；\n",
    "  4. 输入用户查询 → 生成查询向量 → 在 FAISS 中检索语义匹配的文档。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "974ca822",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "分割后的文档数量：3\n",
      "\n",
      "检索到的相似文档：\n",
      "\n",
      "1. 文档内容：LangChain 是一个用于构建大语言模型（LLM）应用的开发框架。\n",
      "它提供了一系列组件，包括 Document Loaders（文档加载）、Text Splitters（文本分割）、\n",
      "   文档元数据：{}\n",
      "\n",
      "2. 文档内容：Embedding Models（嵌入模型）、Vector Stores（向量存储）等。\n",
      "其中，Embedding Models 的作用是将文本转化为向量，以便后续的语义检索。\n",
      "   文档元数据：{}\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_text_splitters import CharacterTextSplitter  # 文本分割组件\n",
    "from langchain_huggingface import HuggingFaceEmbeddings\n",
    "\n",
    "# 1. 步骤 1：准备原始文档并分割（复用 TextSplitter）\n",
    "raw_document = \"\"\"\n",
    "LangChain 是一个用于构建大语言模型（LLM）应用的开发框架。\n",
    "它提供了一系列组件，包括 Document Loaders（文档加载）、Text Splitters（文本分割）、\n",
    "Embedding Models（嵌入模型）、Vector Stores（向量存储）等。\n",
    "其中，Embedding Models 的作用是将文本转化为向量，以便后续的语义检索。\n",
    "DeepSeek 是一款开源大模型，其配套的嵌入模型对中文语义理解有较好的支持，\n",
    "适合用于中文场景的 RAG 应用开发。\n",
    "\"\"\"\n",
    "\n",
    "# 分割文档（按字符分割， chunk_size=100，chunk_overlap=10）\n",
    "text_splitter = CharacterTextSplitter(\n",
    "    chunk_size=100,\n",
    "    chunk_overlap=10,\n",
    "    separator=\"\\n\"\n",
    ")\n",
    "split_docs = text_splitter.create_documents([raw_document])  # 生成 Document 对象列表\n",
    "print(f\"分割后的文档数量：{len(split_docs)}\")\n",
    "\n",
    "# 2. 步骤 2：初始化 Embedding 模型\n",
    "embeddings = HuggingFaceEmbeddings(\n",
    "    model_name = \"../../embed_model/bge-large-zh-v1.5\",  # 首次会下载该模型（这里我们提前手动下载好了）\n",
    "    model_kwargs = {\"device\": \"cpu\"},  # 可指定\"cuda\"使用GPU,\n",
    "    encode_kwargs = {\"normalize_embeddings\": True}  # 归一化向量，提升相似度计算准确性\n",
    ")\n",
    "\n",
    "# 3. 步骤 3：将分割后的文档与向量存入 FAISS\n",
    "# 方式 1：直接从 Document 列表构建 FAISS 索引\n",
    "db = FAISS.from_documents(split_docs, embeddings)\n",
    "\n",
    "# 方式 2：若已有文本列表，也可从文本构建（需手动关联元数据）\n",
    "# texts = [doc.page_content for doc in split_docs]\n",
    "# metadatas = [doc.metadata for doc in split_docs]\n",
    "# db = FAISS.from_texts(texts, embeddings, metadatas=metadatas)\n",
    "\n",
    "# 4. 步骤 4：语义检索（查询与文档匹配）\n",
    "query = \"LangChain 中的 Embedding 组件有什么作用？\"\n",
    "# 检索 Top 2 个最相似的文档\n",
    "retrieved_docs = db.similarity_search(query, k=2)\n",
    "\n",
    "# 输出检索结果\n",
    "print(\"\\n检索到的相似文档：\")\n",
    "for i, doc in enumerate(retrieved_docs, 1):\n",
    "    print(f\"\\n{i}. 文档内容：{doc.page_content}\")\n",
    "    print(f\"   文档元数据：{doc.metadata}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
